import math
import os
import sys

import cv2 as cv
import numpy as np


def show_direction(img_dir):
    print("-----------------------------------")

    img = cv.imread(img_dir)
    if img is None:
        sys.exit("could not read the image.")
    # 灰度转换
        lower = np.array([40, 65, 29])
    upper = np.array([255, 255, 255])
    lined_img = cv.inRange(img, lower, upper)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    # 高斯模糊处理
    gray = cv.blur(gray, (10, 10))

    # 无法用于不存在白线的情况，故更换参数后废弃，保留用以参考解题过程
    # ret, thresh = cv.threshold(
    #     gray, 100, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C + cv.THRESH_OTSU)
    ret, thresh = cv.threshold(
        gray, 100, 255, cv.THRESH_BINARY)

    # 一开始打算先用颜色对照片处理，但后来发现直接改为二值图像更为有效

    # 定义卷积层
    kernel = np.ones((8, 8), np.uint8)
    # 形态学变换函数去除噪声
    bit_img = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=1)

    # 检测明显的中断及白线不存在（旧），检测整个背景的轮廓，
    # 查看其最小正接矩形面积是否接近图片面积
    # 对于小部分白线中断情况无法正确判断

    # 获取白色元素轮廓，并取其中最大的，代表了白线的轮廓
    # 是考虑到所有图片中白线都是除背景外面积最大的元素
    # 用这个方法来剔除干扰元素的影响或许较为合适

    cnts, _ = cv.findContours(
        bit_img, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
    if len(cnts) <= 0:
        print(img_dir+" go foward because Contour not found. ")
        print("\n-----------------------------------\n")
        return

    c = sorted(cnts, key=cv.contourArea, reverse=True)[0]

    # 获取包围轮廓的最小无旋转矩形
    x, y, w, h = cv.boundingRect(c)
    # 测试
    cv.rectangle(img, (x, y), (
        x + w, y + h), (0, 255, 0), 2, 8)
    # 获取白线偏离画面中心的偏移量
    offest = ((2*x+w)-img.shape[1])/2
    # 根据最小矩形切割图片，避免干扰
    crop_img = img[y:y+h, x:x+w]  # 测试用
    crop_bit_img = bit_img[y:y+h, x:x+w]
    # 透视变形会使出发端的白线宽度大于结束端，以此判断图像是否倒立
    # 对小部分情况无法正确判断

    # 方向检测
    # （在实际应用中几乎毫无作用，仅仅针对题目里给出的被反转的图片）
    if count_reverse(crop_bit_img) > 3.5:
        crop_bit_img = cv.flip(crop_bit_img, -1)
        crop_img = cv.flip(crop_img, -1)  # 测试用
        img = cv.flip(img, -1)
        offest = -offest
    # cv.imshow("crop_img2", crop_img)
    offest_rate = offest/img.shape[1]/2
    # 边缘检测
    line_img = cv.Canny(crop_bit_img, 100, 200, apertureSize=5)

    # test
    test_imshow(line_img, 50)
    # test

    lines = cv.HoughLinesP(line_img, 1.5, np.pi/180, 78,
                           minLineLength=65, maxLineGap=130)
    if (lines) is None:
        print("NO LINE identify")
        return

    # 将线段针对y值进行排序，直观表示为越靠后的线段在图片上的位置越靠近顶部
    lines = sorted(lines, key=lambda x: x[0][3], reverse=True)
    rate, Curvature = count_Curvature(lines)
    print(rate, Curvature)

    if Curvature == 0:
        print(img_dir+" go foward because Curvature's halt.")
        print("\n-----------------------------------\n")
        return

    # 最终转向值由线条的平均弯曲率和偏离中心程度决定
    # 当-2<temp<2为向前，temp<-2为向右，反之向左
    # 值越大则需要转向的程度越大
    temp = Curvature*0.4 + offest_rate*90*0.4 + rate*0.4

    # print(Curvature,offest_rate)
    print(img_dir, end=' ')
    if temp < -2:
        print("turn left", end='')
    elif temp > 2:
        print("turn right", end='')
    else:
        print("go foward", end='')
    print(" with "+str(temp))
    print("\n-----------------------------------\n")

    # test
    temp_img = crop_img.copy()
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv.line(temp_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
    test_imshow(temp_img, 50)


def count_Curvature(lines):
    '''
    输入：概率霍夫变换函数的返回值,即线段集
    输出：1：白线的整体角度
         2：线的平均变化量，绝对值越大变化越大，负值为左
    '''
    Curvature = 0
    i = 0
    x1, y1, x2, y2 = lines[0][0]
    angel = math.atan((y1 - y2) / (x2 - x1))*180/3.14 if x2-x1 != 0 else 90
    if angel < 0:
        angel += 180
    sum_angel = 0
    for line in lines:
        i += 1
        x1, y1, x2, y2 = line[0]
        angel = (math.atan((y1 - y2) / (x2 - x1))*180 /
                 3.14) if x2-x1 != 0 else 90
        if angel < 0:
            angel += 180
        sum_angel += angel
        if(abs(sum_angel/i-angel) > 80):
            # print("halt")
            return 0, 0
        Curvature += (sum_angel/i-angel)
    return -(sum_angel/i-90), Curvature/i
    # print(Curvature)


def test_imshow(temp_img, scale_percent):
    '''
    用于测试的函数，将按照百分比缩放的图片显示出来
    '''
    width = int(temp_img.shape[1] * scale_percent / 100)
    height = int(temp_img.shape[0] * scale_percent / 100)
    dim = (width, height)
    cv.imshow("img", cv.resize(
        temp_img, dim, interpolation=cv.INTER_AREA))
    cv.waitKey(0)
